﻿/*
 * OpenDoPE Word Add-In
 * Copyright (C) 2010-2011  Plutext Pty Ltd
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
 * 
 * For details, see http://www.gnu.org/licenses/gpl-2.0.html
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using NLog;

using Word = Microsoft.Office.Interop.Word;
using Office = Microsoft.Office.Core;


namespace OpenDoPE_Wed
{
    class XPathPartUtils
    {

        static Logger log = LogManager.GetLogger("XPathPartUtils");

        public static xpathsXpath getByID(
            Office.CustomXMLPart xpathsPart,
            string xpathId)
        {
            xpaths xpaths = new xpaths();
            xpaths.Deserialize(xpathsPart.XML, out xpaths);

            return getByID(xpaths, xpathId);
        }

        private static xpathsXpath getByID(
            xpaths xpaths,
            string xpathId)
        {
            foreach (xpathsXpath item in xpaths.xpath)
            {
                if (item.id.Equals(xpathId))
                {
                    return item;
                }
            }
            return null;
        }


        /// <summary>
        /// Stuff required to create XPath element
        /// (if an entry with this ID exists already,
        /// it will be overwritten)
        /// </summary>
        /// <param name="xpath"></param>
        /// <param name="storeItemID"></param>
        /// <param name="prefixMappings"></param>
        /// <param name="questionID"></param>
        public static string updateXPathsPart(
            Office.CustomXMLPart xpathsPart,
            string xpath, 
            string xpathId,
            string storeItemID, string prefixMappings,
                                        string questionID)
        {
            //Office.CustomXMLPart xpathsPart = ((WedTaskPane)this.Parent.Parent.Parent).xpathsPart;

            xpaths xpaths = new xpaths();
            xpaths.Deserialize(xpathsPart.XML, out xpaths);

            xpathsXpath item = getByID(xpaths, xpathId);
            xpathsXpathDataBinding db = null;
            if (item == null)
            {
                item = new xpathsXpath();
                item.id = xpathId; //System.Guid.NewGuid().ToString(); 
                db = new xpathsXpathDataBinding();
                item.dataBinding = db;
                xpaths.xpath.Add(item);
            }
            else
            {
                db = item.dataBinding;
            }

            if (!string.IsNullOrWhiteSpace(questionID))
                item.questionID = questionID;
             
            db.xpath = xpath;
            db.storeItemID = storeItemID;
            if (!string.IsNullOrWhiteSpace(prefixMappings))
                db.prefixMappings = prefixMappings;

            // Save it in docx
            string result = xpaths.Serialize();
            log.Info(result);
            CustomXmlUtilities.replaceXmlDoc(xpathsPart, result);

            return item.id;

        }

        public static void fixDupeIDs(Office.CustomXMLPart xpathsPart)
        {
            if (System.Windows.Forms.MessageBox.Show(
                "This utility will search for an remove old XPaths left by versions of this AddIn before 9 Sept 2011.  Click OK to continue at your own risk.",
                "Warning", System.Windows.Forms.MessageBoxButtons.OKCancel)
                    == System.Windows.Forms.DialogResult.Cancel)
            {
                return;
            }

            xpaths xpaths = new xpaths();
            xpaths.Deserialize(xpathsPart.XML, out xpaths);

            // Setup: Look at the content controls; for each XPath ID, what bindings have we got?
            Dictionary<String, List<String>> bindingsByID = new Dictionary<string, List<String>>();
            foreach (Word.Range storyRange in Globals.ThisAddIn.Application.ActiveDocument.StoryRanges)
            {
                foreach (Word.ContentControl cc in storyRange.ContentControls)
                {
                    TagData td = new TagData(cc.Tag);
                    String existingID = td.get("od:xpath");
                    if (existingID !=null && cc.XMLMapping.IsMapped)
                    {
                        List<String> entries;
                        try
                        {
                            entries = bindingsByID[existingID];
                        }
                        catch (KeyNotFoundException)
                        {
                            bindingsByID.Add(existingID, new List<String>());
                            entries = bindingsByID[existingID];
                        }
                        entries.Add(cc.XMLMapping.XPath);
                    }
                }
            }

            // Setup: Look at the XPaths; for each XPath ID, what xpaths have we got?
            Dictionary<String, List<xpathsXpath>> xpathsByID = new Dictionary<string, List<xpathsXpath>>();
            foreach (xpathsXpath item in xpaths.xpath)
            {
                String existingID = item.id;
                List<xpathsXpath> entries;
                try
                {
                    entries = xpathsByID[existingID];
                }
                catch (KeyNotFoundException)
                {
                    xpathsByID.Add(existingID, new List<xpathsXpath>());
                    entries = xpathsByID[existingID];
                }
                entries.Add(item);
            }

            xpaths result = new xpaths();

            StringBuilder results = new StringBuilder();

            // OK, we're all set up
            foreach (String xpathID in xpathsByID.Keys)
            {
                List<xpathsXpath> entries = xpathsByID[xpathID];
                if (entries.Count > 1)
                {
                    log.Debug("Processing xpath id: " + xpathID + " .. dupes detected");

                    // OK, which is current?
                    List<String> actualBindings;
                    try {
                        actualBindings = bindingsByID[xpathID];
                    } catch (KeyNotFoundException)
                    {
                        // It is not used at all, but its a dupe.
                        // Copy it over, since it might be used in a condition or repeat
                        results.Append("\n\r XPath ID " + xpathID + " is duplicated " + entries.Count + " times, but isn't being fixed automatically, since its not being used in an OpenXML bind.");
                        result.xpath.AddRange(entries);
                        continue;
                    }

                    if (allSame(actualBindings))
                    {
                        bool haveMatch = false;
                        // We know which one we want
                        String expected = actualBindings[0];
                        // Keep the first XPath which matches
                        foreach (xpathsXpath entry in entries)
                        {
                            if (entry.dataBinding.xpath.Equals(expected))
                            {
                                result.xpath.Add(entry);
                                haveMatch = true;
                                log.Debug(".. selected a matching XPath");
                                break;
                            }
                        }
                        if (haveMatch)
                        {
                            results.Append("\n\r XPath ID " + xpathID + " is duplicated .. current only being kept");
                            // Kept above
                        }
                        else 
                        {
                            results.Append("\n\r XPath ID " + xpathID + " is duplicated " + entries.Count + " times, but can't be fixed automatically, since none match the content control binding " + expected);
                            result.xpath.AddRange(entries);
                            continue;
                        }
                    }
                    else // not all same
                    {
                        results.Append("\n\r XPath ID " + xpathID + " is duplicated " + entries.Count + " times, but can't be fixed automatically, since " + actualBindings.Count() + " + content controls contain differing values.");
                        result.xpath.AddRange(entries);
                        continue;
                    }

                }
                else // just one entry :-)
                {
                    log.Debug("Processing xpath id: " + xpathID + " .. OK");
                    results.Append("\n\r XPath ID " + xpathID + " is OK ");
                    result.xpath.AddRange(entries);
                }
            }

            if (System.Windows.Forms.MessageBox.Show(
                "The following changes will be made.  Click Cancel if you are unsure. \n\r\n\r" + results.ToString(),
                "Warning", System.Windows.Forms.MessageBoxButtons.OKCancel)
                    == System.Windows.Forms.DialogResult.Cancel)
            {
                return;
            }

            // Save it in docx
            string xml = result.Serialize();
            log.Info(xml);
            CustomXmlUtilities.replaceXmlDoc(xpathsPart, xml);

            System.Windows.Forms.MessageBox.Show("Done. Save your docx if you are comfortable.");
        }

        private static bool allSame(List<String> actualBindings)
        {
            String expected = actualBindings[0];
            foreach (String s in actualBindings)
            {
                if (!s.Equals(expected)) return false;
            }
            return true;
        }


    }
}
